package com.shujia

object Demo05Func02 {
  // 定义一个函数func01，有两个参数分别是：String、Int，最终返回Int类型，实现将String转换成Int再加上Int
  // (String,Int) => Int
  def func01(str: String, i: Int): Int = {
    str.toInt + i + 100
  }

  // (String,Int) => Int
  def func02(str: String, i: Int): Int = {
    str.toInt + i + 200
  }

  // (Int,Int) => Int
  def func03(i1: Int, i2: Int): Int = {
    i1 + i2 + 300
  }

  // (String,Int) => String
  def func04(str: String, i: Int): String = {
    str + i
  }

  /**
   * 定义一个函数funcX：可以接收一个类型为：(String,Int) => Int 的函数f，返回值类型为Int
   * 功能：在funcX内部调用函数f并且加上100再进行返回
   *
   */
  def funcX(f: (String, Int) => Int): Int = {
    f("100", 200) + 100
  }

  def funcX2(f: String => Int): Int = {
    f("100") + 123
  }

  /**
   * 匿名函数的定义：没有名字函数就称之为匿名函数
   * (参数名1:类型,参数名2:类型,参数名3:类型,......) => {函数体}
   */
  // 将func01改写成匿名函数
  (str: String, i: Int) => {
    str.toInt + i + 100
  }


  def main(args: Array[String]): Unit = {
    /**
     * 面向对象编程：将对象传来传去，会将对象作为参数或者是返回值进行传递，需要注意对象的类型
     * 面向函数编程：将函数传来传去，会将函数作为参数或者是返回值进行传递，需要注意函数的类型
     *
     * 函数的类型和什么没有关系？
     * 1、跟关键字def没有关系
     * 2、跟函数名没有关系
     * 3、跟参数名无关
     * 4、跟函数实现的功能也无关
     *
     * 函数的类型怎么描述？
     * 由参数的类型（类型、个数、顺序）以及返回值的类型一同决定
     *
     * (参数1的类型,参数2的类型,参数3的类型) => 返回值类型
     *
     * 函数式编程：
     * 1、以函数作为参数
     * 2、以函数作为返回值
     */
    println(funcX(func01)) // 500

    println(funcX(
      (str: String, i: Int) => {
        str.toInt + i + 100
      }
    )) // 500

    /**
     * 匿名函数的省略规则：
     * 1、如果代码只有一行，则花括号可以省略
     * 2、如果匿名函数是作为参数传给另一个函数的，则参数的类型可以省略
     * 3、如果匿名函数只有一个参数，则括号可以省略
     * 4、如果匿名函数的参数只被使用了一次，则可以直接用下划线替代，并且 => 及 左边的部分都可以省略
     *
     * 匿名函数一般会结合以函数作为参数的形式进行使用
     *
     */

    println(funcX((str: String, i: Int) => str.toInt + i + 100))
    println(funcX((str, i) => str.toInt + i + 100))
    println(funcX(_.toInt + _ + 100))

    val anonymousFunc: String => Int = (str: String) => str.toInt + 200
    anonymousFunc("100")
    println(funcX2(anonymousFunc))
    println(funcX2((str: String) => str.toInt + 200))
    println(funcX2((str) => str.toInt + 200))
    println(funcX2(str => str.toInt + 200))
    println(funcX2(_.toInt + 200))


    println(funcX(_.toInt + _ + 100)) // 500

    println(funcX(func02)) // 600
    //    funcX(func03) // func03参数的类型不匹配
    //    funcX(func04) // func04返回值的类型不匹配

    val strArr: Array[String] = "Scala,Spark,Flink".split(",")

    /**
     * 函数式编程之以函数作为参数的应用
     * foreach是Array数组类型提供的一个可以遍历数据的方法
     * 需要接收一个函数f：String => Unit
     *
     * println同样也是一个函数：Any => Unit
     */
    strArr.foreach(println)

  }

}
